iT邦幫忙

2023 iThome 鐵人賽

DAY 24
0
Modern Web

30天React練功坊-攻克常見實務/面試問題系列 第 24

2023It 30天React練功坊-攻克常見實務/面試問題 Day24: Creating a custom hook for data fetching(interview question)

  • 分享至 

  • xImage
  •  
tags: ItIron2023 react

前言

昨天我們看了一個基本的Tab component題目,在那個常見的UI組件上做了一點小小的測試,相信對你們來說並不是什麼難題,今天我們換個實務的場景,請你重構一份簡單的程式碼,這類的需求在開發時極為常見,我們馬上開始吧!

本日題目

請你看一下這個codesandbox與下方的程式碼。

import React, { useState, useEffect } from "react";

function App() {
  const [data, setData] = useState(null);
  const [loading, setLoading] = useState(true);
  const [error, setError] = useState(null);
  const url = "https://jsonplaceholder.typicode.com/users/1";

  useEffect(() => {
    const fetchData = async () => {
      setLoading(true);
      try {
        const response = await fetch(url);
        const result = await response.json();
        setData(result);
      } catch (err) {
        setError(err);
      } finally {
        setLoading(false);
      }
    };
    fetchData();
  }, [url]);

  return (
    <div className="App">
      <h1>Creating a custom hook for data fetching</h1>
      {loading && <p>Loading...</p>}
      {error && <p>Error: {error.message}</p>}
      {data && (
        <div>
          <h2>User Info</h2>
          <p>Name: {data.name}</p>
          <p>Username: {data.username}</p>
          <p>Email: {data.email}</p>
          <p>Phone: {data.phone}</p>
          <p>Website: {data.website}</p>

          <h3>Address</h3>
          <p>Street: {data.address.street}</p>
          <p>Suite: {data.address.suite}</p>
          <p>City: {data.address.city}</p>
          <p>Zipcode: {data.address.zipcode}</p>

          <h3>Company</h3>
          <p>Name: {data.company.name}</p>
          <p>Catch Phrase: {data.company.catchPhrase}</p>
          <p>BS: {data.company.bs}</p>
        </div>
      )}
    </div>
  );
}

export default App;

這是一個極為單純組件,我們在組件中發出fetch請求並根據狀態做條件渲染,程式碼本身並沒有任何的問題,畫面可以順利地呈現我們預期中的畫面,如下圖。

day24-demo-image

今天你要做的事情是另外建立一個useFetch的custom hook去封裝fetch的邏輯並達到一模一樣的效果,你所建立的custom hook須滿足以下的條件

  1. 該hook應回傳isLoading, data & error 等值(回傳的格式不限定)
  2. 無須使用任何packages,請用starter file內的程式碼即可
  3. 請勿改變render部分的程式碼,僅封裝fecth相關的邏輯
  4. 你的custom hook會接受一個url做為參數

解答與基本解釋

今天這個題目坦白講遠比昨天的更簡單一些,但卻是更常在實務中碰到的問題,一般來說我們希望組件內的邏輯越單純越好,有些可以透過custom hook封裝的邏輯我們會希望另外處理,這麼一來也會提高一些維護性,同時讓你的程式碼可以被其他組件複用,因此這也經常是一個熱門的問題,我們馬上來看一下其中一個做法吧!

一般來說這類的custom hook都會另外用獨立的檔案管理,因此通常我會期待看到一個新的檔案叫useFetch.js,其中我會把與fecth相關的邏輯都移過來統一處理。

import { useState, useEffect } from 'react';

const useFetch = (url) => {
  const [data, setData] = useState(null);
  const [loading, setLoading] = useState(true);
  const [error, setError] = useState(null);

  useEffect(() => {
    const fetchData = async () => {
      setLoading(true);
      try {
        const response = await fetch(url);
        const result = await response.json();
        setData(result);
      } catch (err) {
        setError(err);
      } finally {
        setLoading(false);
      }
    };

    fetchData();
  }, [url]);

  return { data, loading, error };
};

export default useFetch;

你可以發現幾乎就是把之前在App組件的那一包整個搬過來而已,唯一你需要注意的是return的部分,這裡其實你要用陣列的形式回傳也是可以的,完全就看你怎麼設計。
剩下的部分就非常簡單了(雖然剛剛的部分我想也是簡單得不得了),你只要在App組件內引入該hook即可。

import useFetch from './useFetch';

function App() {
  const url = 'https://jsonplaceholder.typicode.com/users/1';
  const { data, loading, error } = useFetch(url);

  return (
    <div className="App">
      <h1>Fetch Data</h1>
      {loading && <p>Loading...</p>}
      {error && <p>Error: {error.message}</p>}
      {data && (
        <div>
          <h2>User Info</h2>
          <p>Name: {data.name}</p>
          <p>Username: {data.username}</p>
          <p>Email: {data.email}</p>
          <p>Phone: {data.phone}</p>
          <p>Website: {data.website}</p>

          <h3>Address</h3>
          <p>Street: {data.address.street}</p>
          <p>Suite: {data.address.suite}</p>
          <p>City: {data.address.city}</p>
          <p>Zipcode: {data.address.zipcode}</p>

          <h3>Company</h3>
          <p>Name: {data.company.name}</p>
          <p>Catch Phrase: {data.company.catchPhrase}</p>
          <p>BS: {data.company.bs}</p>
        </div>
      )}
    </div>
  );
}

export default App;

最終你會看到完全一樣的結果,mission accomplished!

總結

我知道你要說什麼,今天的題目看起來確實挺污辱人智商的,只是把程式碼搬到另一個檔案罷了,不過一提到custom hook許多人就有點慌了,實際上它並不是這麼嚇人的東西,骨子裡還是用你熟悉的那hook,只是為了可讀性以及可複用性而將相關的邏輯封裝起來而已,希望透過今天的例子能讓你未來面對custom hook的情境時多了點自信,雖然往往實務或面試的問題會再更複雜一些,但基本的原理都是相同的!我們明天見囉!

本文章同步發布於個人部落格,有興趣的朋友也可以來逛逛~!


上一篇
30天React練功坊-攻克常見實務/面試問題 Day23: Simple tab component(interview question)
下一篇
30天React練功坊-攻克常見實務/面試問題 Day25: Implement simple pagination(interview question)
系列文
30天React練功坊-攻克常見實務/面試問題30
圖片
  直播研討會
圖片
{{ item.channelVendor }} {{ item.webinarstarted }} |
{{ formatDate(item.duration) }}
直播中

尚未有邦友留言

立即登入留言